using System;
using System.Runtime.CompilerServices;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;

namespace GPUAnimationLib.Runtime
{
    /// <summary>
    /// GPU动画状态更新
    /// （2020.3.48f1如果使用Burst编译只能打成64位包(32位包会闪退，原因不明)，换成2021.3.10f1就无此问题）
    /// </summary>
    [BurstCompile]
    public struct GPUAnimationUpdateJob : IJob
    {
        public NativeParallelHashMap<int, AnimatorData> Animators;
        public NativeList<AnimatorData> AnimatorBuffer;
        public float DeltaTime;

        public void Execute()
        {
            AnimatorBuffer.Clear();
            foreach (var item in Animators)
            {
                if (item.Value.ProcessState == 0) continue;
                AnimatorBuffer.Add(item.Value);
            }

            for (int i = 0; i < AnimatorBuffer.Length; i++)
            {
                var animator = AnimatorBuffer[i];
                if (UpdateAnimator(ref animator, ref animator.State, out var animationState))
                {
                    animator.NeedUpdateToGPU = true;
                    animator.AnimationState = animationState;
                }
                else
                {
                    animator.NeedUpdateToGPU = false;
                }

                Animators[animator.AnimatorId] = animator;
            }
        }

        bool UpdateAnimator(ref AnimatorData animator, ref AnimationState state, out Matrix4x4 animationState)
        {
            if (state.CurrentStopped)
            {
                animationState = default;
                return false;
            }

            if (state.CurrentAnimationData.AnimationId != animator.CurrentAnimationData.AnimationId) //切换动画
            {
                state.PreviousAnimationData = state.CurrentAnimationData;
                state.CurrentAnimationData = animator.CurrentAnimationData;
                state.BlendPreviousToCurrent = 0f;
                state.PreviousNormalizedTime = state.CurrentNormalizedTime;
                state.CurrentNormalizedTime = animator.StartNormalizedTime;
                state.PreviousStopped = false;
                state.CurrentStopped = false;
            }

            UpdateAnimatorState(in animator, state.CurrentAnimationData, ref state.CurrentNormalizedTime,
                ref state.CurrentStopped,
                out float primaryBlendFactor, out float primaryTransitionToNextFrame, out int primaryFrameIndex);

            if (state.BlendPreviousToCurrent >= 1f) //正在播放当前动画
            {
                //float4x4 逐行排列
                // animationState = new float4x4(
                //     primaryBlendFactor, primaryTransitionToNextFrame, primaryFrameIndex, 0,
                //     0, 0, 0, 0,
                //     0, 0, 0, 0,
                //     0, 0, 0, 0);
                //Matrix4x4 逐列设置
                animationState = new Matrix4x4(
                    new Vector4(primaryBlendFactor, 0, 0, 0),
                    new Vector4(primaryTransitionToNextFrame, 0, 0, 0),
                    new Vector4(primaryFrameIndex, 0, 0, 0),
                    new Vector4(0, 0, 0, 0));

                //附加物计算
                for (int i = 0; i < animator.AttachmentDatas.Length; i++)
                {
                    var attachment = animator.AttachmentDatas[i];
                    int baseIndex = attachment.AttachmentIndex * animator.TotalFrames;
                    LerpNextFrame(animator.AnchorTransforms, baseIndex, primaryFrameIndex,
                        primaryTransitionToNextFrame, out var currentPos, out var currentRot);
                    attachment.LocalPosition = currentPos;
                    attachment.LocalRotation = currentRot;
                    animator.AttachmentDatas[i] = attachment;
                }

                return true;
            }
            else //动画切换过程
            {
                if (animator.TransitionTime == 0) //无过渡
                {
                    state.BlendPreviousToCurrent = 1f;
                }
                else
                {
                    state.BlendPreviousToCurrent += DeltaTime / animator.TransitionTime;
                    if (state.BlendPreviousToCurrent > 1f) state.BlendPreviousToCurrent = 1f;
                }

                float previousToCurrent = state.BlendPreviousToCurrent; //混合比例（0：last，1：next）
                float currentToPrevious = 1f - previousToCurrent; //混合比例（0：next，1：last）
                UpdateAnimatorState(in animator, state.PreviousAnimationData, ref state.PreviousNormalizedTime,
                    ref state.PreviousStopped,
                    out float previousPrimaryBlendFactor, out float previousPrimaryTransitionToNextFrame,
                    out int previousPrimaryFrameIndex);
                //float4x4 逐行排列
                // animationState = new float4x4(
                //     previousToCurrent * primaryBlendFactor, primaryTransitionToNextFrame, primaryFrameIndex, 0,
                //     currentToPrevious * previousPrimaryBlendFactor, previousPrimaryTransitionToNextFrame,
                //     previousPrimaryFrameIndex, 0,
                //     0, 0, 0, 0,
                //     0, 0, 0, 0);
                //Matrix4x4 逐列设置
                animationState = new Matrix4x4(
                    new Vector4(previousToCurrent * primaryBlendFactor, currentToPrevious * previousPrimaryBlendFactor,
                        0, 0),
                    new Vector4(primaryTransitionToNextFrame, previousPrimaryTransitionToNextFrame, 0, 0),
                    new Vector4(primaryFrameIndex, previousPrimaryFrameIndex, 0, 0),
                    new Vector4(0, 0, 0, 0));

                //附加物计算
                for (int i = 0; i < animator.AttachmentDatas.Length; i++)
                {
                    var attachment = animator.AttachmentDatas[i];
                    int baseIndex = attachment.AttachmentIndex * animator.TotalFrames;
                    LerpNextFrame(animator.AnchorTransforms, baseIndex, primaryFrameIndex,
                        primaryTransitionToNextFrame, out var currentPos, out var currentRot);
                    LerpNextFrame(animator.AnchorTransforms, baseIndex, previousPrimaryFrameIndex,
                        previousPrimaryTransitionToNextFrame, out var previousPos, out var previousRot);
                    LerpTransform(previousPos, previousRot, currentPos, currentRot, previousToCurrent, out currentPos,
                        out currentRot);
                    attachment.LocalPosition = currentPos;
                    attachment.LocalRotation = currentRot;
                    animator.AttachmentDatas[i] = attachment;
                }

                return true;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        void UpdateAnimatorState(in AnimatorData animator, GPUAnimationData animationData, ref float normalizedTime,
            ref bool stopped,
            out float primaryBlendFactor, out float primaryTransitionToNextFrame, out int primaryFrameIndex)
        {
            UpdateAnimationNormalizedTime(in animator, ref normalizedTime, ref stopped, animationData,
                out float transitionToNextFrame, out int relativeFrameIndex);
            primaryBlendFactor = 1; //主混合因子
            primaryTransitionToNextFrame = transitionToNextFrame; //当前实际动画时间 - 离散的动画帧时间（向前取整）
            primaryFrameIndex = animationData.StartIndex + relativeFrameIndex; //实际的动画帧数
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        void UpdateAnimationNormalizedTime(in AnimatorData animator, ref float normalizedTime, ref bool stopped,
            GPUAnimationData animationData,
            out float transitionToNextFrame, out int relativeFrameIndex)
        {
            int endFrame = math.max(1, animationData.Length - 1);
            float animationLength = endFrame / animationData.SampleFrameRate;
            float currentTime = normalizedTime * animationLength;
            if (!stopped) currentTime += DeltaTime * animator.Speed;
            normalizedTime = currentTime / animationLength;
            if (animationData.Loop)
            {
                //循环动画
                while (normalizedTime >= 1f) normalizedTime -= 1f;
            }
            else
            {
                //非循环动画
                if (normalizedTime >= 1f)
                {
                    normalizedTime = 1f;
                    stopped = true;
                }
            }

            if (Math.Abs(normalizedTime - 1f) < 0.001f)
            {
                relativeFrameIndex = endFrame - 1; //相对动画帧索引（==向前取整）
                transitionToNextFrame = 1f;
            }
            else
            {
                float relativeFrameIndexFloat = normalizedTime * endFrame;
                relativeFrameIndex = (int)math.floor(relativeFrameIndexFloat); //相对动画帧索引（向前取整）
                transitionToNextFrame = relativeFrameIndexFloat - relativeFrameIndex; //实际值-最近上一帧值
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        void LerpNextFrame(in UnsafeList<Matrix4x4> config, int baseIndex, int frameIndex, float transitionToNextFrame,
            out float3 pos, out Quaternion rot)
        {
            LerpTransform(config[baseIndex + frameIndex], config[baseIndex + frameIndex + 1], transitionToNextFrame,
                out pos, out rot);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        void LerpTransform(Matrix4x4 valueA, Matrix4x4 valueB, float t, out float3 pos, out Quaternion rot)
        {
            float3 posA = new float3(valueA.m03, valueA.m13, valueA.m23);
            quaternion rotA = new quaternion(valueA);
            float3 posB = new float3(valueB.m03, valueB.m13, valueB.m23);
            quaternion rotB = new quaternion(valueB);
            pos = math.lerp(posA, posB, t);
            rot = math.slerp(rotA, rotB, t);
            //return float4x4.TRS(pos, rot, new float3(1f, 1f, 1f));
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        void LerpTransform(float3 posA, quaternion rotA, float3 posB, quaternion rotB, float t, out float3 pos,
            out Quaternion rot)
        {
            pos = math.lerp(posA, posB, t);
            rot = math.slerp(rotA, rotB, t);
        }
    }
}